﻿using System;
using System.Management;
using System.Net;
using System.Threading;
using network;

namespace FailoverClusterController
{
    /// <summary>
    /// Essa classe implementa a comunicacao e gerenciamento
    /// dos nodes do failover cluster (sabores).
    /// </summary>
    class NonLeader
    {
        UDPclient clientUDP;
        Election election;
        ManagementEventWatcher watcher;
        String hostname = Environment.MachineName;

        private System.Diagnostics.EventLog logger;

        public ManualResetEvent mre = new ManualResetEvent(false);

        public NonLeader(System.Diagnostics.EventLog eventLogger, Election election, IPAddress interfaceIP)
        {
            logger = eventLogger;

            try
            {
                this.clientUDP = new UDPclient(interfaceIP);
            }
            catch (Exception e)
            {
                log("Error getting NIC: " + e.Message);
            }
            this.election = election;
        }

        private void log(string str)
        {
            logger.Source = "NonLeader";
            logger.WriteEntry(str);
        }

        public void start()
        {
            this.setEventVMStateChanged();
        }

        public void stop()
        {
            mre.Set();
        }
        
        public void setEventVMStateChanged()
        {
            // Essa eh a parte que espera por mudancas nas vms
            log("Registrando evento de mudanca de VMs.");

            ManagementScope scope = new ManagementScope(@"root\MSCluster");

            String q = String.Format("SELECT * FROM MSCluster_EventResourceStateChange " +
                    "WHERE EventNode = '{0}' AND " +
                    "(EventNewState = 2 OR EventNewState = 3)", this.hostname);

            WqlEventQuery query = new WqlEventQuery(q);

            // Initialize an event watcher and subscribe to events 
            // that match this query
            this.watcher = new ManagementEventWatcher(scope, query);

            watcher.EventArrived +=
                new EventArrivedEventHandler(this.vmEnvironmentChanged);
            try
            {
                watcher.Start();
            }
            catch (ManagementException e)
            {
                log("WMI Watcher error: " + e.Message);
            }

        }

        void vmEnvironmentChanged(object sender, EventArrivedEventArgs e)
        {
            // Aqui, devemos enviar um sinal pro lider de que o ambiente das vms
            // mudou, e ele precisa avaliar se eh preciso, fazer algum balanceamento

            // Provavelmente aqui que entraria uma chamada caso o lider naum 
            // respondesse de volta e seria preciso convocar uma eleicao.

            ManagementBaseObject evento = e.NewEvent;

            log("\nEvent Fired");
            /*log("VM: {0}, Node {1}, New State: {2}", 
                evento["EventGroup"], evento["EventNode"], evento["EventNewState"]);*/




            string message = String.Format("{0} {1} {2} {3}",
                (int)MsgType.FLAVOR_CHANGED,
                this.hostname,
                evento["EventGroup"],
                evento["EventNewState"]);

            log(String.Format("Sending message to leader: {0}", message));

            try
            {
                this.clientUDP.sendMsg(message, this.election.electionInfo.leader);
            }
            catch (AckTimeout)
            {
                log("Timeout!");
                this.stop();
            }

        }
    }
}
